{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"UTF-8\""
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "### pry-docの読み込み\n",
    "#require \"/root/git_jupyter_notebook/Ruby/vendor/bundle/ruby/2.3.0/gems/pry-doc-0.10.0/lib/pry-doc\"\n",
    "require \"/Users/ftakao2007/jupyter/vendor/bundle/ruby/2.3.0/gems/pry-doc-0.10.0/lib/pry-doc\"\n",
    "### エンコーディングをUTF-8に設定\n",
    "Encoding.default_external = \"UTF-8\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "# インスタンスメソッド\n",
    "\n",
    "* 動作\n",
    "    * インスタンスは、クラスで定義されたインスタンスメソッドを呼び出すことができる\n",
    "    * 継承したクラスのインスタンスは、スーパークラスで定義されたインスタンスメソッドを呼び出すことができる"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## クラスオブジェクト\n",
    "\n",
    "* クラスレイヤとインスタンスレイヤ\n",
    "    * クラスレイヤ\n",
    "        * 例\n",
    "            * クラス(Fooなど)\n",
    "            * クラスオブジェクト(インスタンスメソッド method1など)\n",
    "    * インスタンスレイヤ\n",
    "        * クラスからオブジェクトを生成(インスタンス化)するとインスタンスレイヤに降りてくるというイメージ\n",
    "        * 例\n",
    "            * Fooクラスのインスタンス(foo1, foo2など)\n",
    "                * それぞれのインスタンスのインスタンス変数(@aなど)\n",
    "\n",
    "* 要素\n",
    "    * インスタンスメソッド\n",
    "        * メモリ上では<font color=\"red\">クラスオブジェクトに保持</font>される\n",
    "            * レイヤとしてはクラスレイヤ\n",
    "    * インスタンス変数\n",
    "        * メモリ上では<font color=\"red\">インスタンスに保持</font>される\n",
    "            * レイヤとしてはインスタンスレイヤ\n",
    "                "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## 継承したクラスオブジェクト\n",
    "\n",
    "* Foo : 継承されるクラスオブジェクト\n",
    "    * 継承するクラスより一般的な性質を持つ(汎化)\n",
    "* FooExt : 継承したクラスオブジェクト\n",
    "    * 元のクラスより固有の性質を持つ(特化)\n",
    "\n",
    "* foo1\n",
    "    * Fooクラスで定義された性質を持つ\n",
    "* fooExt1\n",
    "    * Fooクラス及びFooExtクラスで定義された性質を持つ"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "2\n",
      "3\n",
      "12\n",
      "Foo\n"
     ]
    }
   ],
   "source": [
    "class Foo\n",
    "  def initialize(a)\n",
    "    @a = a\n",
    "  end\n",
    "  \n",
    "  def method1\n",
    "    @a\n",
    "  end\n",
    "end\n",
    "\n",
    "### FooExt <-(特化)--(汎化)-> Foo(継承元クラス)\n",
    "class FooExt < Foo\n",
    "  def initialize(a,b)\n",
    "    @b = b\n",
    "    super a\n",
    "  end\n",
    "  \n",
    "  def method2(c)\n",
    "    @a + @b + c\n",
    "  end\n",
    "end\n",
    "\n",
    "### Fooクラスのインスタンスを生成\n",
    "foo1 = Foo.new(1)\n",
    "foo2 = Foo.new(2)\n",
    "\n",
    "### 定義したインスタンスメソッドを実行\n",
    "puts foo1.method1\n",
    "puts foo2.method1\n",
    "\n",
    "### FooExtクラスのインスタンスを生成\n",
    "fooExt1 = FooExt.new(3, 4)\n",
    "\n",
    "### 定義したインスタンスメソッドを実行\n",
    "puts fooExt1.method1\n",
    "puts fooExt1.method2(5)  \n",
    "\n",
    "### スーパークラスの取得\n",
    "### FooExt -(superclass)-> Foo\n",
    "puts FooExt.superclass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## メソッドの探索経路\n",
    "\n",
    "### メソッドが存在する場合\n",
    "\n",
    "1. foo1インスタンスのインスタンスメソッドmethod1を実行\n",
    "1. インタプリタがそれが属するクラス(Foo)のオブジェクトに指定されたメソッド(method1)があるかどうかを判定\n",
    "    * 抽象度を一つ上げて(クラスレイヤで)Fooのクラスオブジェクトにmethod1が存在するかを判定している\n",
    "1. 同様にfooExt1インスタンスのmethod2メソッドが実行された場合、クラスレイヤのFooExtクラスにmethod2が存在するかを判定\n",
    "1. メソッドがある場合は正常に実行される\n",
    "\n",
    "### メソッドが存在しない場合\n",
    "\n",
    "1.  インスタンスメソッド実行\n",
    "1. そのクラスのクラスオブジェクト探す\n",
    "1. スーパークラスのクラスオブジェクトを順にたどって探す\n",
    "1. 最後まで見つからなければ例外NoMethodErrorを発生\n",
    "\n",
    "### メソッド探索を一言で言うと\n",
    "\n",
    "* レシーバのクラスに入り、メソッドを見つけるまで継承チェーンを上ること"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "undefined method `method2' for #<Foo:0x007fa3f1966e90 @a=1>\n"
     ]
    }
   ],
   "source": [
    "class Foo\n",
    "  def initialize(a)\n",
    "    @a = a\n",
    "  end\n",
    "  \n",
    "  def method1\n",
    "    @a\n",
    "  end\n",
    "end\n",
    "\n",
    "class FooExt < Foo\n",
    "  def initialize(a,b)\n",
    "    @b = b\n",
    "    super a\n",
    "  end\n",
    "  \n",
    "  def method2(c)\n",
    "    @a + @b + c\n",
    "  end\n",
    "end\n",
    "\n",
    "### fooExt1にmethod1は存在しないが、継承元にはあるのでそちらが実行される\n",
    "fooExt1 = FooExt.new(1,2)\n",
    "puts fooExt1.method1\n",
    "\n",
    "### Fooにmethod2は存在しないのでNoNameError\n",
    "foo1 = Foo.new(1)\n",
    "begin\n",
    "  puts foo1.method2\n",
    "rescue NoMethodError => ex\n",
    "  puts ex\n",
    "end"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "### method_missing\n",
    "\n",
    "* メソッドが見つからないとき\n",
    "    * Objectクラスのmethod_misssingメソッド(クラスメソッド?)が呼び出される(NoMethodError)\n",
    "* method_misssingメソッド\n",
    "    * デフォルト\n",
    "        * 第一引数 : 指定されたメソッド名\n",
    "        * 第二引数以降 : 指定された引数\n",
    "* method_missingを上書きすることでメソッドが見つからない場合の動作をフックすることができる\n",
    "* 参考\n",
    "    * OSS等でどこにもメソッドが定義されていない場合\n",
    "        1. method_missingでフックされている\n",
    "        2. 動的にメソッドを生成している"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "called: no_method\n",
      "[1, nil, true, :a, \"hoge\"]\n",
      "\n",
      "undefined method `no_method' for #<Fuga:0x007fa3f13b58c0>\n"
     ]
    }
   ],
   "source": [
    "class Fuga\n",
    "  def method_missing(m, *args)\n",
    "    puts \"called: \" + m.to_s\n",
    "    puts args\n",
    "    puts \n",
    "\n",
    "    ### superで例外が発生するようにスーパークラスのmethod_missingを呼び出す\n",
    "    begin\n",
    "      super\n",
    "    rescue NoMethodError => ex\n",
    "      puts ex\n",
    "    end\n",
    "    \n",
    "  end\n",
    "end\n",
    "\n",
    "Fuga.new.no_method(1, nil, true, :a, \"hoge\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## レシーバ\n",
    "\n",
    "* 呼び出すメソッドが属するオブジェクト\n",
    "    * my_string.reverse()があったらmy_stringがレシーバ"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## 継承チェーン(継承順)\n",
    "\n",
    "* スーパークラスを省略してクラスを定義\n",
    "    * <font color=\"red\">Objectクラスが継承される</font>\n",
    "        * Ruby1.9以降ではObjectクラスはBasicObjectクラスを継承するように変更されている\n",
    "            * BasicObjectクラスはRuby1.9より前のObjectクラスからほとんどのメソッドを取り除いたクラス\n",
    "            * プログラム作成上ではBasicObjectクラスを意識する必要はないが試験では継承チェーンの問題で出題される事がある\n",
    "        * ObjectクラスとBasicObjectクラスの間にKernelモジュールがある\n",
    "        * FooクラスはObjectクラスを継承している\n",
    "* クラス継承チェーンの参照\n",
    "    * クラス継承チェーン一覧\n",
    "        * ancestorsメソッド\n",
    "    * あるクラスが継承チェーンンに含まれているか確認\n",
    "        * 比較演算子"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[Foo, Object, PP::ObjectMixin, JSON::Ext::Generator::GeneratorMethods::Object, Kernel, BasicObject]\n",
      "[FooExt, Foo, Object, PP::ObjectMixin, JSON::Ext::Generator::GeneratorMethods::Object, Kernel, BasicObject]\n",
      "true\n",
      "false\n",
      "true\n"
     ]
    }
   ],
   "source": [
    "### スーパークラスを略さずクラスを定義\n",
    "class Foo < Object\n",
    "  def initialize(a)\n",
    "    @a = a\n",
    "  end\n",
    "  \n",
    "  def method1\n",
    "    @a\n",
    "  end\n",
    "end\n",
    "\n",
    "class FooExt < Foo\n",
    "  def initialize(a,b)\n",
    "    @b = b\n",
    "    super a\n",
    "  end\n",
    "  \n",
    "  def method2(c)\n",
    "    @a + @b + c\n",
    "  end\n",
    "end\n",
    "\n",
    "### クラス継承チェーン一覧の表示\n",
    "### Jupyter環境なので途中でPP::ObjectMixin, JSON::Ext::Generator::GeneratorMethods::Objectが表示されている\n",
    "puts Foo.ancestors\n",
    "puts FooExt.ancestors\n",
    "\n",
    "### 比較演算子でクラスが継承チェーンに含まれているかを確認\n",
    "### 不等号がクラスの大小関係(包含関係)を表していると覚える\n",
    "puts Foo < Object\n",
    "puts Foo < FooExt\n",
    "puts Foo > FooExt"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 継承チェーンに現れるモジュール\n",
    "\n",
    "* ancestorsメソッドではKernelモジュールが現れる\n",
    "    * モジュールをクラスや別モジュールにインクルードするとき\n",
    "        * 無名クラスを作ってモジュールをラップして継承チェーンに挿入される\n",
    "        * それはインクルードするクラスの真上に入る\n",
    "        * このラッパークラスは「インクルードクラス」や「プロキシクラス」と呼ばれる\n",
    "* インクルードクラス\n",
    "    * superclassメソッドはインクルードクラスが存在しないかのようにふるまう\n",
    "    * 通常のRubyコードではインクルードクラスにアクセスできない\n",
    "* Kernelモジュール\n",
    "    * 4-6.MethodVisualization.ipynb を参照"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## オブジェクトが持つインスタンスメソッドとインスタンス変数の調査\n",
    "\n",
    "* インスタンスメソッドの調査\n",
    "    * instance_methodsメソッド\n",
    "        * 引数にfalseを指定するとスーパークラスをたどらない\n",
    "* インスタンス変数の調査\n",
    "    * instance_valuesメソッド\n",
    "* 参考\n",
    "    * insance_valuesメソッドはKernelモジュールのインスタンスメソッド\n",
    "    * instance_methodsメソッドはクラスメソッド？"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[:public_instance_methods, :instance_methods, :private_instance_methods, :protected_instance_methods]"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Module.instance_methods(false).grep(/instance_methods/)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[:method1]\n",
      "[:method2]\n",
      "[:@a]\n",
      "[:@b, :@a]\n",
      "引数にfalseを指定しない場合は継承元クラスのインスタンスメソッドが全て表示される\n",
      "[:method2, :method1, :pry, :__binding__, :pretty_print, :pretty_print_cycle, :pretty_print_instance_variables, :pretty_print_inspect, :to_json, :instance_of?, :public_send, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :private_methods, :kind_of?, :instance_variables, :tap, :define_singleton_method, :is_a?, :extend, :to_enum, :enum_for, :<=>, :pretty_inspect, :===, :=~, :!~, :eql?, :respond_to?, :freeze, :inspect, :display, :object_id, :send, :gem, :to_s, :method, :public_method, :singleton_method, :nil?, :hash, :class, :singleton_class, :clone, :dup, :itself, :taint, :tainted?, :untaint, :untrust, :trust, :untrusted?, :methods, :protected_methods, :frozen?, :public_methods, :singleton_methods, :!, :==, :!=, :__send__, :equal?, :instance_eval, :instance_exec, :__id__]\n",
      "\n",
      "BasicObjectのインスタンスメソッド\n",
      "[:!, :==, :!=, :__send__, :equal?, :instance_eval, :instance_exec, :__id__, :__binding__]\n",
      "\n",
      "Kernelモジュールのインスタンスメソッド(ここにinstance_variablesがある)\n",
      "[:instance_of?, :public_send, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :private_methods, :kind_of?, :instance_variables, :tap, :define_singleton_method, :is_a?, :extend, :to_enum, :enum_for, :<=>, :pretty_inspect, :===, :=~, :!~, :eql?, :respond_to?, :freeze, :inspect, :display, :object_id, :send, :gem, :to_s, :method, :public_method, :singleton_method, :nil?, :hash, :class, :singleton_class, :clone, :dup, :itself, :taint, :tainted?, :untaint, :untrust, :trust, :untrusted?, :methods, :protected_methods, :frozen?, :public_methods, :singleton_methods]\n",
      "\n",
      "Objectのインスタンスメソッド\n",
      "[:pry, :__binding__]\n",
      "\n",
      "instance_methodsはクラスメソッドなのでこれらの中には含まれていない？\n"
     ]
    }
   ],
   "source": [
    "class Foo\n",
    "  def initialize(a)\n",
    "    @a = a\n",
    "  end\n",
    "  \n",
    "  def method1\n",
    "    @a\n",
    "  end\n",
    "end\n",
    "\n",
    "class FooExt < Foo\n",
    "  def initialize(a,b)\n",
    "    @b = b\n",
    "    super a\n",
    "  end\n",
    "  \n",
    "  def method2(c)\n",
    "    @a + @b + c\n",
    "  end\n",
    "end\n",
    "\n",
    "### インスタンスメソッド\n",
    "puts Foo.instance_methods(false)\n",
    "puts FooExt.instance_methods(false)\n",
    "\n",
    "### インスタンス変数\n",
    "foo1 = Foo.new(1)\n",
    "fooExt1 = FooExt.new(1,2)\n",
    "puts foo1.instance_variables\n",
    "puts fooExt1.instance_variables\n",
    "\n",
    "puts \"引数にfalseを指定しない場合は継承元クラスのインスタンスメソッドが全て表示される\"\n",
    "puts FooExt.instance_methods\n",
    "puts\n",
    "\n",
    "puts \"BasicObjectのインスタンスメソッド\"\n",
    "puts BasicObject.instance_methods(false)\n",
    "puts\n",
    "\n",
    "puts \"Kernelモジュールのインスタンスメソッド(ここにinstance_variablesがある)\"\n",
    "puts Kernel.instance_methods(false)\n",
    "puts\n",
    "\n",
    "puts \"Objectのインスタンスメソッド\"\n",
    "puts Object.instance_methods(false)\n",
    "puts\n",
    "\n",
    "puts \"instance_methodsはクラスメソッドなのでこれらの中には含まれていない？\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## メソッドに別名をつける\n",
    "\n",
    "* 別名をつける\n",
    "    * alias <新メソッド名> <旧メソッド名>\n",
    "* 定義を取り消す(別名を消す)\n",
    "    * undef <メソッド名>\n",
    "* 挙動\n",
    "    * 評価された時点でのメソッド定義が対象\n",
    "        * 定義される前にaliasやundefを書いてもNameErrorが発生する"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[:fuga1, :fuga3]\n"
     ]
    }
   ],
   "source": [
    "class Hoge\n",
    "  def fuga1; end\n",
    "  def fuga2; end\n",
    "  alias fuga3 :fuga1\n",
    "  undef fuga2\n",
    "end\n",
    "\n",
    "puts Hoge.instance_methods(false)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## オープンクラス\n",
    "\n",
    "* オープンクラス\n",
    "    * 定義済みのクラスを再定義できるクラスのこと\n",
    "   　　* 既存のクラスを再オープンしてその場で修正できる技術\n",
    "        * StringやArrayのような標準クラスも含まれる\n",
    "* クラスの再オープン\n",
    "    * 一度定義したクラスを再定義のために開く事\n",
    "* 既存の標準クラスも再定義できる\n",
    "    * Stringなど\n",
    "    * 問題点\n",
    "        * 元々のクラスに存在しているメソッド名とかぶってしまった場合、そのメソッドを上書きしてしまう\n",
    "        * 標準クラスはプログラム全体で有効となるので注意が必要\n",
    "* classキーワードはクラス宣言というよりはスコープ演算子のようなもの\n",
    "    * classの主な仕事はクラスのコンテキストに連れて行くこと\n",
    "        * そのコンテキストでメソッドを定義する\n",
    "        * 存在しないクラスを作成するのはむしろ副作用と言ってもいいかもしれない"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hogeクラス定義1回目のメソッドfuga4が存在する\n",
      "[:fuga4, :fuga1, :fuga3]\n",
      "\n",
      "既存のStringクラスにfuga5メソッドを追加\n",
      "[:fuga5, :freeze, :force_encoding, :to_f]\n"
     ]
    }
   ],
   "source": [
    "### Hogeクラス定義1回目\n",
    "class Hoge\n",
    "  def fuga4; end\n",
    "end\n",
    "\n",
    "### Hogeクラス定義2回目\n",
    "class Hoge\n",
    "  def fuga1; end\n",
    "  def fuga2; end\n",
    "  alias fuga3 :fuga1\n",
    "  undef fuga2\n",
    "end\n",
    "\n",
    "puts \"Hogeクラス定義1回目のメソッドfuga4が存在する\"\n",
    "puts Hoge.instance_methods(false)\n",
    "puts\n",
    "\n",
    "class String\n",
    "  def fuga5; end\n",
    "end\n",
    "\n",
    "puts \"既存のStringクラスにfuga5メソッドを追加\"\n",
    "puts String.instance_methods(false).grep(/f/)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "クラスを3回定義しているわけではない\n",
      "Hello\n",
      "Hello\n",
      "Hello\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "puts \"クラスを3回定義しているわけではない\"\n",
    "3.times do\n",
    "  class C\n",
    "    puts \"Hello\"\n",
    "  end\n",
    "end"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x\n",
      "y\n"
     ]
    }
   ],
   "source": [
    "class D     ### この段階ではまだクラスは存在していない\n",
    "  def x; 'x'; end   ### このクラス定義の中に入ったときにクラスは定義される。メソッドxも一緒に定義される\n",
    "end\n",
    "\n",
    "class D   ### 上で定義したクラスDを再オープンしている\n",
    "  def y; 'y'; end  ### メソッドyを追加している\n",
    "end\n",
    "\n",
    "obj = D.new\n",
    "puts obj.x\n",
    "puts obj.y"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## スーパークラスを指定して再オープン\n",
    "\n",
    "* スーパークラスを指定して再オープン\n",
    "    * スーパークラスはオープンする前のクラスと同じでなければならない"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [],
   "source": [
    "### クラスFoo, BarとFooから継承したクラスBazを定義\n",
    "class Foo; end\n",
    "class Bar; end\n",
    "class Baz < Foo\n",
    "end\n",
    "\n",
    "### BarはBazのスーパークラスではないので再オープンできない\n",
    "#class Baz < Bar\n",
    "#end\n",
    "\n",
    "### FooはBazのスーパークラスなので再オープンできる\n",
    "class Baz < Foo\n",
    "end\n",
    "\n",
    "### Bazは再オープンできる\n",
    "class Baz\n",
    "end"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "# メタプログラミングRuby\n",
    "\n",
    "## 1.3.1 オブジェクトの中味\n",
    "\n",
    "* インスタンス変数は値を代入したときにはじめて出現する\n",
    "    * Java等の静的言語とは違いオブジェクトのインスタンス変数はクラスと何のつながりもない\n",
    "* インスタンス変数はオブジェクトに住んでいる\n",
    "* オブジェクトのメソッドはオブジェクトのクラスに住んでいる\n",
    "    * なので、同じクラスのオブジェクトでもインスタンス変数は共有されない"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "MyClass\n",
      "[]\n",
      "1\n",
      "[:@v]\n"
     ]
    }
   ],
   "source": [
    "class MyClass\n",
    "  def my_method\n",
    "    @v = 1\n",
    "  end\n",
    "end\n",
    "\n",
    "obj = MyClass.new\n",
    "puts obj.class\n",
    "puts obj.instance_variables  ### my_methodを呼び出す前の段階ではインスタンス変数は存在していない\n",
    "puts obj.my_method   ### 値を代入されたこの段階でインスタンス変数が出現する\n",
    "puts obj.instance_variables"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[:my_method]"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "### 上で定義したmy_methodが含まれている\n",
    "obj.methods.grep(/my/)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "true\n",
      "false\n"
     ]
    }
   ],
   "source": [
    "### クラスメソッドとインスタンスメソッド\n",
    "puts String.instance_methods == \"abc\".methods\n",
    "puts String.methods == \"abc\".methods"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Ruby 2.3.1",
   "language": "ruby",
   "name": "ruby"
  },
  "language_info": {
   "file_extension": ".rb",
   "mimetype": "application/x-ruby",
   "name": "ruby",
   "version": "2.3.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
